package onvif

import (
	"context"
	"encoding/xml"
	"errors"
	"fmt"
	onvifdevice "github.com/use-go/onvif/device"
	"github.com/use-go/onvif/xsd"
	xsdOnvif "github.com/use-go/onvif/xsd/onvif"
	"go.uber.org/zap"
	"io"
	"time"
)

type TimeZone struct {
	TZ xsd.Token `xml:"TZ"`
}

type SystemDateTimeExtension xsd.AnyType

type GetSystemDateAndTimeResponse struct {
	SystemDateAndTime SystemDateTime
}

type TimesT struct {
	XMLName xml.Name `xml:"Time"`
	Hour    int      `xml:"Hour"`
	Minute  int      `xml:"Minute"`
	Second  int      `xml:"Second"`
}

type DatesT struct {
	XMLName xml.Name `xml:"Date"`
	Year    int      `xml:"Year"`
	Month   int      `xml:"Month"`
	Day     int      `xml:"Day"`
}

type DateTime struct {
	Time TimesT `xml:"Time"`
	Date DatesT `xml:"Date"`
}

type SystemDateTime struct {
	DateTimeType    string
	DaylightSavings bool
	TimeZone        TimeZone
	UTCDateTime     DateTime  `xml:"UTCDateTime"`
	LocalDateTime   *DateTime `xml:"LocalDateTime"`
	Extension       SystemDateTimeExtension
}

type Envelope struct {
	Header struct{}
	Body   struct {
		GetSystemDateAndTimeResponse GetSystemDateAndTimeResponse
	}
}

// SetSystemDateAndTime 同步摄像头系统时间
func (o *Onvif) SetSystemDateAndTime(ctx context.Context, cc *CameraConnection) (err error) {
	defer func() {
		if err != nil {
			o.log.Error("SetSystemDateAndTime error", zap.Object("cc", cc), zap.Error(err))
		} else {
			o.log.Debug(fmt.Sprintf("SetSystemDateAndTime "), zap.Object("cc", cc))
		}
	}()
	dev, err := o.newDevice(cc)
	if err != nil {
		return err
	}
	now := time.Now().UTC()
	stSetDateAndTimeReq := onvifdevice.SetSystemDateAndTime{
		DaylightSavings: false,
		DateTimeType:    "Manual",
		TimeZone:        xsdOnvif.TimeZone{TZ: "CST-8"},
		UTCDateTime: xsdOnvif.DateTime{
			Time: xsdOnvif.Time{
				Hour:   xsd.Int(now.Hour()),
				Minute: xsd.Int(now.Minute()),
				Second: xsd.Int(now.Second())},
			Date: xsdOnvif.Date{
				Year:  xsd.Int(now.Year()),
				Month: xsd.Int(now.Month()),
				Day:   xsd.Int(now.Day()),
			},
		},
	}
	resp, err := dev.CallMethod(stSetDateAndTimeReq)
	if err != nil {
		return err
	}
	//b, _ := io.ReadAll(resp.Body)
	//defer resp.Body.Close()
	//fmt.Println(string(b), err)
	if resp.StatusCode != 200 {
		return errors.New("SetSystemDateAndTime error")
	}
	//fmt.Println(resp.Status)
	//b, _ := io.ReadAll(resp.Body)
	//fmt.Println(string(b), err)
	//resp, err := sdkDevice.Call_SetSystemDateAndTime(ctx, dev, stSetDateAndTimeReq)
	//fmt.Println(err, resp)
	return nil
}

// GetSystemDateAndTime 获取摄像头时间, 可用于检测设备是否在线
func (o *Onvif) GetSystemDateAndTime(ctx context.Context, cc *CameraConnection) (dt time.Time, err error) {
	//解析时间，先解析本地时间
	sdat, err := o.getSystemDateAndTime(ctx, cc)
	if err != nil {
		return
	}
	if sdat.LocalDateTime != nil {
		dt = time.Date(sdat.LocalDateTime.Date.Year, time.Month(sdat.LocalDateTime.Date.Month), sdat.LocalDateTime.Date.Day, sdat.LocalDateTime.Time.Hour, sdat.LocalDateTime.Time.Minute, sdat.LocalDateTime.Time.Second, 0, o.loc)
		return
	}
	//按标准解析
	loc, _ := time.LoadLocation("UTC")
	dt = time.Date(sdat.UTCDateTime.Date.Year, time.Month(sdat.UTCDateTime.Date.Month), sdat.UTCDateTime.Date.Day, sdat.UTCDateTime.Time.Hour, sdat.UTCDateTime.Time.Minute, sdat.UTCDateTime.Time.Second, 0, loc)
	//在设置本地时区
	dt = dt.In(o.loc)
	return
}

func (o *Onvif) getSystemDateAndTime(ctx context.Context, cc *CameraConnection) (dt SystemDateTime, err error) {
	defer func() {
		if err != nil {
			o.log.Error("GetSystemDateAndTime error", zap.Object("cc", cc), zap.Error(err))
		} else {
			o.log.Debug(fmt.Sprintf("GetSystemDateAndTime resp:%v", dt), zap.Object("cc", cc))
		}
	}()
	dev, err := o.newDevice(cc)
	if err != nil {
		return
	}
	retDatetime := onvifdevice.GetSystemDateAndTime{}
	//rsp, err := sdkDevice.Call_GetSystemDateAndTime(ctx, dev, retDatetime)
	rsp, err := dev.CallMethod(retDatetime)
	if err != nil {
		return
	}
	defer rsp.Body.Close()
	b, err := io.ReadAll(rsp.Body)
	if err != nil {
		return
	}
	var reply Envelope
	err = xml.Unmarshal(b, &reply)
	if err != nil {
		return
	}
	return reply.Body.GetSystemDateAndTimeResponse.SystemDateAndTime, nil
}
